import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.List;


public class ServerTest extends JFrame implements Runnable {
    public ServerTest() throws IOException { //IOException是I/O 操作失败或中断产生的异常
        setTitle("服务器");
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        count = 0;
        ss = new ServerSocket(12345);

        //InetAddress addr = InetAddress.getLocalHost(); 获取不到真实ip  获取到的是虚拟机ip
        InetAddress addr = getLocalHostExactAddress();
        String ip = addr.getHostAddress();
        JLabel ipLable = new JLabel("服务器IP：" + ip);
        ipLable.setFont(new Font("微软雅黑", Font.BOLD, 24));
        this.add(ipLable, BorderLayout.NORTH);
        this.pack();


        //新创建一个 昵称-蛇 键值对
        //在服务端父线程创建一个 昵称-蛇 键值对是必要的 这直接保证了各服务子线程的数据同步
        nameSnakeMap = Collections.synchronizedMap(new HashMap<String, Snake>());
        gameMap = new GameMap();
        //ServerSocket是等待客户端的请求，一旦获得一个连接请求，就创建一个Socket示例来与客户端进行通信。
        new Thread(this).start();

    }


    @Override //表示重写
    public void run() {
        try {
            while (true) {
                s = ss.accept();
                System.out.println("新客户请求接入中...");
                System.out.println("已接收到" + (count + 1) + "个客户请求");
                ServerThread st = new ServerThread(s, nameSnakeMap,gameMap);
                threads.add(st);
                st.start();
                count++;
            }
        } catch (Exception e) { //Exception是异常类
            e.printStackTrace(); //在控制台打印出异常种类，错误信息和出错位置等
        }
    }

    //https://cloud.tencent.com/developer/article/1610919
    //获取真实ip
    public static InetAddress getLocalHostExactAddress() {
        try {
            InetAddress candidateAddress = null;

            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface iface = networkInterfaces.nextElement();
                // 该网卡接口下的ip会有多个，也需要一个个的遍历，找到自己所需要的
                for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
                    InetAddress inetAddr = inetAddrs.nextElement();
                    // 排除loopback回环类型地址（不管是IPv4还是IPv6 只要是回环地址都会返回true）
                    if (!inetAddr.isLoopbackAddress()) {
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址，就是它了 就是我们要找的
                            // ~~~~~~~~~~~~~绝大部分情况下都会在此处返回你的ip地址值~~~~~~~~~~~~~
                            return inetAddr;
                        }

                        // 若不是site-local地址 那就记录下该地址当作候选
                        if (candidateAddress == null) {
                            candidateAddress = inetAddr;
                        }

                    }
                }
            }

            // 如果出去loopback回环地之外无其它地址了，那就回退到原始方案吧
            return candidateAddress == null ? InetAddress.getLocalHost() : candidateAddress;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws IOException {
        ServerTest test = new ServerTest();
    }

    ServerThread t;
    Integer count; //客户数量
    ArrayList<ServerThread> threads = new ArrayList<ServerThread>(); //线程数组
    Map<String, Snake> nameSnakeMap;//昵称-蛇 键值对
    ServerSocket ss = null;
    Socket s = null;
    GameMap gameMap;


}

class ServerThread extends Thread {

    public ServerThread(Socket s, Map<String, Snake> map, GameMap gameMap) {
        this.s = s;
        nickName = null;
        nameSnakeMap = map;
        this.gameMap = gameMap;
        try {
            in = s.getInputStream();
            out = s.getOutputStream();
            ois = new ObjectInputStream(in);
            oos = new ObjectOutputStream(out);
            firstConnect();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
/*
    首次连接的任务：
    1.接受到昵称
    2.发送游戏地图*/
    private void firstConnect() throws IOException, ClassNotFoundException {
        nickName = (String) ois.readObject();
        System.out.println("第一次连接");
        System.out.println("昵称是" + nickName);
        oos.writeObject(gameMap);
    }

    public void getMsg() throws IOException, ClassNotFoundException {
        DataInputStream dis = new DataInputStream(in);
        boolean isEating = dis.readBoolean();
        Snake snake = (Snake) ois.readObject();
        nameSnakeMap.put(nickName, snake);
        if(isEating){
            System.out.println("在吃了");
            gameMap.getFood().generate(nameSnakeMap.values(),gameMap.getWall());
            System.out.println("食物更新成功");
        }

    }

    public void sendMsg() {
        try {
            oos.reset();
            oos.writeObject(gameMap);//先发送地图
            oos.flush();
            oos.reset();
            oos.writeObject(nameSnakeMap);
            oos.flush();

        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                getMsg();
                sendMsg();
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("连接中断，删除玩家" + nickName);
            nameSnakeMap.remove(nickName);
        }

    }


    Socket s;
    Map<String, Snake> nameSnakeMap;//昵称-分数 键值对
    InputStream in;
    OutputStream out;
    ObjectInputStream ois;
    ObjectOutputStream oos;
    String nickName;
    GameMap gameMap; //地图

}
